package net
{
	import events.P2pEvent;
	
	import flash.display.Sprite;
	import flash.events.NetStatusEvent;
	import flash.net.GroupSpecifier;
	import flash.net.NetConnection;
	import flash.net.NetGroup;
	import flash.net.NetStream;
	import flash.net.Responder;
	import flash.utils.Dictionary;
	
	import game.P2pWorld;
	
	import net.decoder.DecoderClient;
	import net.decoder.DecoderServer;
	
	public class P2p extends Sprite
	{
		/**
		 *方便调用自己的实例的 静态变量
		 */
		public static var inst:P2p;
		
		public static const NONE:String = "None";//p2p还没设置完类型
		public static const GROUP_MEMBER:String = "Group Member";// netGroup中的一个成员
		public static const SERVER:String = "Server";// p2p网络类型设置为虚拟服务器，负责所理所有客户端和自己的交互逻辑
		public static const CLIENT:String = "Client";//p2p网络类型设置客户端，只是接收到由虚拟服务器发来的通知后作相应处理
		
		//////////////////////////////////////////////////////////
		private var _type:String;
		private var p2pNc:NetConnection;
		private var _setDone:Boolean=false;//是否已连接上NetGroup
		private var _connected:Boolean = false;//是否已连接上p2p服务器
		
		private var decoderClient:DecoderClient;//在本机上的操作都用DecoderClient来统一执行
		
		//类型为GROUP_MEMBER时用到的参数
		private var group:NetGroup;
		private var decoderGroup:DecoderClient;
		private var messageID:uint=0;
		
		//类型为SERVER或CLIENT时用到的参数
		private var _peerId:String;//自己在p2p网络上的peerID
		private var _sender:NetStream;//发送流
		private var _recvStream:NetStream;//作为客户端时的接收流，收到服务端发来的信息时，相应的处理方法在DecoderClient类里
		private var _recvStreams:Dictionary = new Dictionary;//作为服务端时的接收流，接收所有客户端发来的信息，收到客户端发来的信息时，相应的处理方法在DecoderServer类里
		
		//计算ping值用的
		public var lastTime:int;
		public var currentTime:int;
		
		//////////////////////////////////////////////////////////////
		public function get connected():Boolean
		{
			return _connected;
		}
		public function get setDone():Boolean
		{
			return _setDone;
		}
		
		public function get sender():NetStream
		{
			return _sender;
		}
		public function get recvStreams():Dictionary
		{
			return _recvStreams;
		}
		public function get peerID():String
		{
			return _peerId;
		}
		public function get type():String
		{
			return _type;
		}
		//////////////////////////////////////////////////////////
		public function P2p(serverIP:String = "//127.0.0.1")
		{
			if(!inst)
			{
				inst = this;
				
				decoderClient = new DecoderClient;
				
				p2pNc = new NetConnection();
				p2pNc.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
				p2pNc.connect("rtmfp:"+serverIP);
			}else throw new Error("只能实例化一个");
		}
		////////////实例化后给自己加个P2pEvent.CONNECTED_TO_SERVER的侦听，收到侦听后才能设置type ////////////////
		public function set type(Type:String):void
		{
			if(_connected)
			{
				switch(Type)
				{
					case GROUP_MEMBER:
						_type = Type;
						joinNetGroup();
						break;
					case SERVER:
						_type = Type;
						beServer()
						break;
					case CLIENT:
						_type = Type;
						beClient();
						break;
					case NONE:
						_type = Type;
						quit();
						break;
					
					default:
						break;
				}
			}else throw new Error("没链接上服务器");
		}
		
		/**
		 * 在点对点或点对多的网络类型中，将自己链接给作为虚拟服务器的主机，自己作为客户端
		 */		
		public function connectToServer(peerID:String):void
		{
			if(_connected && type == CLIENT && !_setDone)
			{
				_recvStream = new NetStream(p2pNc,peerID);
				_recvStream.client = new DecoderClient;
				_recvStream.bufferTimeMax = 3;
				_recvStream.play("media");
				
				_setDone = true;
				
			}else throw new Error("没链接到rtmfp服务器,或不是客户端类型");
		}
		
		/**
		 * 发送消息都调用这个方法
		 */	
		public function call(functionName:String,values:Array):void
		{
			if(type==P2p.SERVER||type==P2p.GROUP_MEMBER)decoderClient[functionName](values);
			sendMessage(functionName,values);
		}
			
		private function sendMessage(functionName:String,values:Array):void
		{
			if(_setDone)
			{
				if(_type==GROUP_MEMBER)
				{
					//注意，post方法传递出的Object如果和曾经传过的完全一样，则这个Object不会被传出去，so~给发出的信息加个id来解决
					messageID++;
					var message:Object = new Object;
					message.id = messageID;
					message.fn = functionName;
					message.values = values;
					
					//转换一个peerID的群组地址，以适合使用sendToNearest()方法(没搞太懂这个，不加这个就感觉接收那边有问题)
					message.sender = group.convertPeerIDToGroupAddress(p2pNc.nearID);
					group.post(message);
					
				}else if(_type==SERVER ||_type==CLIENT)
				{
					sender.send(functionName,values);//为了在NetGroup中NetStream中有一个统一的收取方法，所以把这里发出去的参数设成了一个数组
				}
			}
		}
		
		
		/**
		 * 退出链接,作用与type = P2p.NONE同样
		 */		
		public function quit():void
		{
			_type = NONE;
			
			_setDone = false;
			
			if(group)
			{
				group.removeEventListener(NetStatusEvent.NET_STATUS,netStatus);
				group.close();
				group = null;
			}
			
			if(_sender)_sender.close();
			if(_recvStream)_recvStream.close();
			for each(var rs:NetStream in _recvStreams)rs.close();
		}
		
		///////////////////////////////////////////////////////////
		private function netStatus(e:NetStatusEvent):void
		{
			switch(e.info.code)
			{
				case "NetConnection.Connect.Success":
					_connected=true;
					_peerId = p2pNc.nearID;
					Global.phpServer.call("userServer.updatePeerID", new Responder(null),Global.uid,_peerId);//将自己的peerID记录到php服务器上去
					dispatchEvent(new P2pEvent(P2pEvent.CONNECTED_TO_SERVER));
					break;
				
				//当netGroup网络连接设置妥当后，向外发出通知
				case "NetGroup.Connect.Success":
					_setDone = true;
					dispatchEvent(new P2pEvent(P2pEvent.GROUP_CONNECTED));
					break;
				
				//当获取到群组post群发通知的时候把信息发给decoder作统一处理
				case "NetGroup.Posting.Notify":
					var message:Object = e.info.message;
					var fnName:String = message.fn;
					var values:Array = message.values;
					decoderGroup[fnName](values);
					break;
				
				default:
					break;
			}
		}
		/**
		 * 加入到NetGroup的群组中
		 */		
		private function joinNetGroup():void
		{
			if(_connected)
			{
				if(_setDone)quit();
				
				_type = GROUP_MEMBER;
				
				messageID = 0;
				
				decoderGroup = new DecoderClient;
				
				//构造函数中的"String"为群组标识，可以随意字符串代替,用于限制和成员授权等信息。
				var groupspec:GroupSpecifier = new GroupSpecifier("lastCitadel");
				
				//这两个是群发设置
				groupspec.postingEnabled = true;
				groupspec.ipMulticastMemberUpdatesEnabled = true;
				
				//将相关的群组加入某个IP地址并侦听相关的端口,Multicast的IP地址需要从不低于224开始，端口需高于1024 ————“224.0.0.0:1024”
				//为保证唯一性，一般设置高一些比较可靠：）
				groupspec.addIPMulticastAddress("225.109.0.58:1408");//按上面乱写
				
				//在已成功的连接nc上创建一个群组
				//groupspecWithAuthorizations:返回一个包含授权的加密的groupspec字符串, 传给NetGroup的构造函数。
				group = new NetGroup(p2pNc,groupspec.groupspecWithAuthorizations());
				
				//为群组增加事件侦听
				group.addEventListener(NetStatusEvent.NET_STATUS,netStatus);
				
			}else throw new Error("没链接到rtmfp服务器");
		}
		
		/**
		 * 在点对点或点对多的网络类型中，将自己设置为虚拟服务器
		 */		
		private function beServer():void
		{
			if(_connected)
			{
				if(_setDone)quit();
				
				_type = SERVER;
				
				_sender = new NetStream(p2pNc, NetStream.DIRECT_CONNECTIONS);
				_sender.bufferTimeMax = 3;
				_sender.publish("media");
				
				var client:Object = new Object();
				client.onPeerConnect = function(comeIn:NetStream):void//作为服务器时，有人连自己的话就自动对连过去
				{
					var rs:NetStream = new NetStream(p2pNc, comeIn.farID);
					rs.bufferTimeMax = 3;
					_recvStreams[comeIn.farID]=rs;//给接收流加上对应客户端peerID的标识（就算客户端断开重新连上也不会新增多的接收流，而是覆盖掉原来的）
					rs.client = new DecoderServer();
					rs.play("media");
				};
				
				_sender.client = client;
				
				_setDone = true;
			}else throw new Error("没链接到rtmfp服务器");
		}
		
		private function beClient():void
		{
			if(_connected)
			{
				if(_setDone)quit();
				_setDone = false;
				
				_type = CLIENT;
				
				_sender = new NetStream(p2pNc, NetStream.DIRECT_CONNECTIONS);
				_sender.bufferTimeMax = 3;
				_sender.publish("media");
				
				
			}else throw new Error("没链接到rtmfp服务器");
		}
		
	}
}